package scheme;
import accessstruct.AccessControlParameter;
import accessstruct.lsss.LSSSLW10Engine;
import accessstruct.lsss.LSSSPolicyEngine;
import accessstruct.parser.ParserUtils;
import accessstruct.parser.PolicySyntaxException;
import it.unisa.dia.gas.jpbc.PairingParameters;
import util.ConfigFileName;
import accessstruct.Tool;
import accessstruct.Node;
import it.unisa.dia.gas.jpbc.Element;
import it.unisa.dia.gas.jpbc.Pairing;
import it.unisa.dia.gas.plaf.jpbc.pairing.PairingFactory;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import util.TreeNode;
import util.TreeUtil;
import static accessstruct.Tool.loadPropFromFile;
import scheme.UserManageBT;

/**
 * CA
 * 主要功能：
 * 1.系统初始化
 */
public class CA {

    private static HashMap<Element,String> AL  = new HashMap<>();
    static List<TreeNode> RL = new ArrayList<>();
    /**CA初始化*/
    public static long setup(ConfigFileName config,UserManageBT BT,int n) {
        Properties AAskProp = new Properties();
        Tool.storePropToFile(AAskProp, config.getAASKFileName());
        Properties AApkProp = new Properties();
        Tool.storePropToFile(AApkProp, config.getAAPKFileName());
        Properties CSPUAkProp = new Properties();
        Tool.storePropToFile(CSPUAkProp, config.getCSPUAKFileName());
        Properties UserTKProp = new Properties();
        Tool.storePropToFile(UserTKProp, config.getTKFileName());
        Properties CSPUAKProp = new Properties();
        Tool.storePropToFile(CSPUAKProp, config.getCSPUAKFileName());
        Properties RSUTKProp = new Properties();
        Tool.storePropToFile(RSUTKProp, config.getTKFileName());
        Properties ctProp = new Properties();
        Tool.storePropToFile(ctProp, config.getCtFileName());
        Properties CApkProp = new Properties();//
        Tool.storePropToFile(CApkProp, config.getGPFileName());
        Properties USKProp = new Properties();
        Tool.storePropToFile(USKProp, config.getUserSKFileName());
        Properties ALProp = new Properties();
        Tool.storePropToFile(ALProp, config.getAccountListFileName());

        long startTime = System.currentTimeMillis();//
        Pairing bp = PairingFactory.getPairing(config.getPairingParametersFileName());//
        Element g = bp.getG1().newRandomElement().getImmutable();//
        Element w = bp.getG1().newRandomElement().getImmutable();//
        Element v = bp.getG1().newRandomElement().getImmutable();//
        long endTime = System.currentTimeMillis();//
        //创建用户管理二叉树
        BT.setBT(TreeUtil.createTree(n,config,g));
        //计算最小覆盖集
        BT.setMCS(TreeUtil.KUNNodes(BT.getBT(),new ArrayList<>()));

        System.out.println(endTime - startTime);
        //生成系统公钥和私钥并存入prop文件之中
        Properties GPProp = loadPropFromFile(config.getGPFileName());
        //公共参数
        GPProp.setProperty("g", Base64.getEncoder().withoutPadding().encodeToString(g.toBytes()));//
        GPProp.setProperty("w", Base64.getEncoder().withoutPadding().encodeToString(w.toBytes()));//
        GPProp.setProperty("v", Base64.getEncoder().withoutPadding().encodeToString(v.toBytes()));//
        //开始存储
        Tool.storePropToFile(GPProp, config.getGPFileName());
        return endTime - startTime;
    }
    /**CA生成追责标识*/
    public static long AccountKeyGen(ConfigFileName config,int aaNums,UserManageBT BT,int uid,String userID) {
        Pairing bp = PairingFactory.getPairing(config.getPairingParametersFileName());
        //用户私钥
        Properties UserSkProp = Tool.loadPropFromFile(config.getUserSKFileName());
        //追责列表
        Properties ALProp = Tool.loadPropFromFile(config.getAccountListFileName());
        //全局参数
        Properties GPProp = Tool.loadPropFromFile(config.getGPFileName());

        //追责标识
        Element accLebal = bp.getGT().newOneElement().getImmutable();
        //取w
        String wString = GPProp.getProperty("w");//
        Element w = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(wString)).getImmutable();

        Element x_uid = BT.getBT().get(uid).getXi().getImmutable();
        Element t = bp.getZr().newRandomElement().getImmutable();
        //计算path（uid）
        //对路径上的所有节点求值
        List<TreeNode> Path_uid = TreeUtil.getPath(BT.getBT().get(uid));
        for(TreeNode node_i : Path_uid)
        {
            Element x_i = node_i.getXi().getImmutable();
            Element x_it =  x_i.mul(t).getImmutable();
            UserSkProp.setProperty("x_it-"+node_i.getId()+"-"+userID, Base64.getEncoder().withoutPadding().encodeToString(x_it.toBytes()));//
        }

        long startTime = System.currentTimeMillis();//
        //开始生成追责标识
        for(int aid = 1 ; aid <= aaNums;aid++) {
            String L_0_String = UserSkProp.getProperty("L0-" + aid+"-"+userID);
            Element L_0 = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(L_0_String)).getImmutable();//正确
            String L_1_String = UserSkProp.getProperty("L1-" + aid+"-"+userID);
            Element L_1 = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(L_1_String)).getImmutable();//正确

            Element K_u = L_0.powZn(x_uid.invert()).getImmutable();
            UserSkProp.setProperty("K_u-"+aid+"-"+userID, Base64.getEncoder().withoutPadding().encodeToString(K_u.toBytes()));//
            accLebal = bp.pairing(L_1,w).getImmutable();
        }

        long endTime = System.currentTimeMillis();//
        ALProp.setProperty("AccLabel-"+userID, Base64.getEncoder().withoutPadding().encodeToString(accLebal.toBytes()));
        AL.put(accLebal,userID);
        System.out.println("时间："+(endTime - startTime));
        //开始存储
        Tool.storePropToFile(ALProp, config.getAccountListFileName());
        Tool.storePropToFile(UserSkProp, config.getUserSKFileName());
        Tool.storePropToFile(GPProp, config.getGPFileName());
        return endTime - startTime;
    }

    /**
     * 生成追责密文
     */
    public static long AccountEncrypt(ConfigFileName config,  String[] attInAcp, int aaNums,Element Message, AccessControlParameter ACP,UserManageBT BT) throws NoSuchAlgorithmException, PolicySyntaxException {

        /*********************************************获取公共参数*******************************************************/
        //准备工作
        Pairing bp = PairingFactory.getPairing(config.getPairingParametersFileName());
        //获取全局参数GP
        Properties CAPKProp = Tool.loadPropFromFile(config.getGPFileName());
        Properties ACtProp = new Properties();
        Properties AAPKProp = Tool.loadPropFromFile(config.getAAPKFileName());

        String gString = CAPKProp.getProperty("g");
        Element g = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(gString)).getImmutable();
        String wString = CAPKProp.getProperty("w");
        Element w = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(wString)).getImmutable();
        String vString = CAPKProp.getProperty("v");
        Element v = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(vString)).getImmutable();
        /**********************************************制定访问策略*****************************************************/
        //随选秘密值s
        Element s = bp.getZr().newRandomElement().getImmutable();//与密文有关
        Element s_acc = bp.getZr().newRandomElement().getImmutable();

//        Element zero = bp.getZr().newZeroElement().getImmutable();//与属性认证有关
        //进行秘密共享

//        Map<String, Element> omegas = LSSSPolicyEngine.secretSharing(bp,zero,ACP);
        /************************************************计算密文*******************************************************/
        long EncryptStartTime = System.currentTimeMillis();
        //计算T_j
        List<TreeNode> MCS = BT.getMCS();
        for(TreeNode node_i : MCS)
        {
            Element T_j = node_i.getYi().powZn(s_acc).getImmutable();
            ACtProp.setProperty("T_j-"+node_i.getId(), Base64.getEncoder().withoutPadding().encodeToString(T_j.toBytes()));//
        }
        //计算C
        Element C = bp.getGT().newOneElement().getImmutable();
        //计算C_0;
        Element C0 = g.powZn(s).getImmutable();
        ACtProp.setProperty("C0", Base64.getEncoder().withoutPadding().encodeToString(C0.toBytes()));

        //开始计算C1 ：Ci1；Ci2: Ci3
        int avLen = attInAcp.length/aaNums;
        for(int aid = 1 ; aid <= aaNums;aid++){
            String[] attOfAA = new String[avLen];
            for(int j = 0;j<avLen;j++){
                //AA-aid 负责分发密钥所对应的属性
                attOfAA[j] = attInAcp[(aid-1)*avLen + j];
            }

            int [][] accessPolicy = ParserUtils.GenerateAccessPolicy(Tool.convertToAcP(attOfAA));
            String[] rho = ParserUtils.GenerateRhos(Tool.convertToAcP(attOfAA));
            AccessControlParameter AcP = LSSSLW10Engine.generateAccessControl(accessPolicy ,rho);

            Map<String, Element> lambdas =LSSSPolicyEngine.secretSharing(bp,s_acc,AcP);

            //计算C
            String eggbetaString = AAPKProp.getProperty("egg_beta-"+aid);
            Element eggBeta = bp.getGT().newElementFromBytes(Base64.getDecoder().decode(eggbetaString)).getImmutable();
            C = C.mul(eggBeta.powZn(s));
            //计算C_1
            String galphaString = AAPKProp.getProperty("g_alpha-"+aid);
            Element g_alpha = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(galphaString)).getImmutable();
            Element C1 = g_alpha.powZn(s).getImmutable();
            ACtProp.setProperty("C1-"+aid, Base64.getEncoder().withoutPadding().encodeToString(C1.toBytes()));
            for (String att:attOfAA) {

                //随机选取l个参数z_i \in Zp
                Element z_i = bp.getZr().newRandomElement().getImmutable();
                //属性值
                byte[] attHash = Tool.sha1(att);
                Element rhoI = bp.getZr().newElementFromHash(attHash, 0, attHash.length).getImmutable();

                Element C_i_1 = w.powZn(lambdas.get(att)).mul(v.invert().powZn(z_i)).getImmutable();
                Element C_i_2 = g.powZn(lambdas.get(att)).mul(g.invert().powZn(z_i).powZn(rhoI)).getImmutable();
                Element C_i_3 = g.powZn(z_i).getImmutable();

                ACtProp.setProperty("C" + att + "1-"+aid, Base64.getEncoder().withoutPadding().encodeToString(C_i_1.toBytes()));
                ACtProp.setProperty("C" + att + "2-"+aid, Base64.getEncoder().withoutPadding().encodeToString(C_i_2.toBytes()));
                ACtProp.setProperty("C" + att + "3-"+aid, Base64.getEncoder().withoutPadding().encodeToString(C_i_3.toBytes()));

            }
        }
        //存储，密文C
        Element CtC = Message.mul(C);
        ACtProp.setProperty("C", Base64.getEncoder().withoutPadding().encodeToString(CtC.getImmutable().toBytes()));
        long EncryptEndTime = System.currentTimeMillis();
        //存储密文
        ACtProp.setProperty("Trap",Base64.getEncoder().withoutPadding().encodeToString(s_acc.sub(s).toBytes()));
        Tool.storePropToFile(ACtProp, config.getAccountCtFileName());
        System.out.println("Step 数据加密阶段结束========================================================================");
        return EncryptEndTime- EncryptStartTime;
    }

    /**
     * 用户黑盒追责
     */
    public static String blackBoxDecrypt(ConfigFileName config, String[] userAtts, String userID,int uid, int aaNums,Element Message,AccessControlParameter ACP,UserManageBT BT)
            throws Exception {
        /*********************************************获取公共参数*******************************************************/
        //准备工作获取公参
        Pairing bp = PairingFactory.getPairing(config.getPairingParametersFileName());
        Properties ActProp = accessstruct.Tool.loadPropFromFile(config.getAccountCtFileName());
        Properties UserSKProp = accessstruct.Tool.loadPropFromFile(config.getUserSKFileName());

        Element D = bp.getGT().newOneElement().getImmutable();
        Map<String, Element> mui = LSSSPolicyEngine.reconstructOmegas(bp,userAtts,ACP);

        String TrapString = ActProp.getProperty("Trap");
        Element Trap = bp.getZr().newElementFromBytes(Base64.getDecoder().decode(TrapString)).getImmutable();
        //获取密文C
        String CString = ActProp.getProperty("C");
        Element C = bp.getGT().newElementFromBytes(Base64.getDecoder().decode(CString)).getImmutable();

        String C_0String = ActProp.getProperty("C0");
        Element C0 = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(C_0String)).getImmutable();


        List<TreeNode> Path_uid = TreeUtil.getPath(BT.getBT().get(uid));
        List<TreeNode> MCS = BT.getMCS();
        long startTime = System.currentTimeMillis();
        MCS.retainAll(Path_uid);
        String node_j  = MCS.get(0).getId();
        String xjttString = UserSKProp.getProperty("x_it-"+node_j+"-"+userID);
        Element xjt = bp.getZr().newElementFromBytes(Base64.getDecoder().decode(xjttString)).getImmutable();//
        String xuidttString = UserSKProp.getProperty("x_it-"+uid+"-"+userID);
        Element xuidt= bp.getZr().newElementFromBytes(Base64.getDecoder().decode(xuidttString)).getImmutable();//
        Element epsilon = xuidt.div(xjt).getImmutable();
        String T_jString = ActProp.getProperty("T_j-"+node_j);
        Element T_j = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(T_jString)).getImmutable();//
        for(int aid = 1; aid <= aaNums;aid++){
            String KString = UserSKProp.getProperty("K-"+aid+"-"+userID);
            Element K = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(KString)).getImmutable();//
            String K_uString = UserSKProp.getProperty("K_u-"+aid+"-"+userID);
            Element K_u = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(K_uString)).getImmutable();//
            String L_0_String = UserSKProp.getProperty("L0-"+aid+"-"+userID);
            Element L0 = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(L_0_String)).getImmutable();//
            String L_1_String = UserSKProp.getProperty("L1-"+aid+"-"+userID);
            Element L1 = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(L_1_String)).getImmutable();//
            String C_1String = ActProp.getProperty("C1-"+aid);
            Element C1 = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(C_1String)).getImmutable();//
            //先计算分子
            Element E = bp.pairing(K_u,T_j).powZn(epsilon).mul(bp.pairing(K, C1)).getImmutable();
            Element F = bp.getGT().newOneElement().getImmutable();
            int averageLen =  userAtts.length/aaNums;
            String [] attInAid = new String[averageLen];
            for(int j = 0;j<averageLen;j++){
                attInAid[j] = userAtts[(aid-1)*averageLen + j];
            }
            for(String att : attInAid){
                //获取密钥
                String K_i_String = UserSKProp.getProperty("K"+att+"-"+aid+"-"+userID);
                Element Ki = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(K_i_String)).getImmutable();
                //获取密文
                String C_i_1String = ActProp.getProperty("C" + att + "1-"+aid);
                Element Ci1 = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(C_i_1String)).getImmutable();
                String C_i_2String = ActProp.getProperty("C" + att + "2-"+aid);
                Element Ci2 = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(C_i_2String)).getImmutable();
                String C_i_3String = ActProp.getProperty("C" + att + "3-"+aid );
                Element Ci3 = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(C_i_3String)).getImmutable();
                F = F.mul(bp.pairing(L1, Ci1).mul(bp.pairing(L0, Ci2)).mul(bp.pairing(Ki, Ci3))).getImmutable();
            }
            D = D.mul(E.div(F)).getImmutable();
        }
        Element m_t = C.div(D).getImmutable();
        Element Trace_uid = m_t.div(Message).powZn(Trap.invert()).getImmutable();

        System.out.println("Key: "+ Trace_uid);
        for (Map.Entry<Element, String> entry : AL.entrySet()) {
            if(entry.getKey().isEqual(Trace_uid)){
                System.out.println("Key: "+ entry.getKey());
                System.out.println("恶意用户是："+ entry.getValue());
            }
        }
        long endTime = System.currentTimeMillis();
        return  AL.get(Trace_uid);
    }

    /**
     * 用户撤销算法
     */
    public static long userRevoke(ConfigFileName config, UserManageBT BT,String uid) throws NoSuchAlgorithmException {

        long onlineEncryptStartTime = System.currentTimeMillis();

        RL.add(BT.getBT().get(Integer.parseInt(uid)));
        for(TreeNode node :RL){
            System.out.println("用户撤销列表RL中的用户是:" + node.getId());
        }
        BT.updateMCS(TreeUtil.KUNNodes(BT.getBT(),RL));

        long onlineEncryptEndTime = System.currentTimeMillis();
        return onlineEncryptEndTime - onlineEncryptStartTime;
    }
    public static long CACtUpdate(ConfigFileName config, UserManageBT BT) throws NoSuchAlgorithmException {
        Pairing bp = PairingFactory.getPairing(config.getPairingParametersFileName());
        Properties updateCTProp = new Properties();
        Element delt = bp.getZr().newRandomElement().getImmutable();
        List<TreeNode> lastMCS= BT.getLastMCS();
        List<TreeNode> MCS= BT.getMCS();
        for(TreeNode node:MCS){
            System.out.println("MCS中的节点为："+node.getId());
        }
        long onlineEncryptStartTime = System.currentTimeMillis();
        for(TreeNode node_j :lastMCS)
        {
            System.out.println("lastMCS:"+node_j.getId());
            Element x_i = node_j.getXi().getImmutable();
            Element tau_j =  x_i.mul(delt);
            updateCTProp.setProperty("tau_j-"+node_j.getId(), Base64.getEncoder().withoutPadding().encodeToString(tau_j.toBytes()));//
        }
        for(TreeNode node_jt :MCS)
        {
            Element x_i = node_jt.getXi().getImmutable();
            Element tau_j_t =  x_i.mul(delt);
            updateCTProp.setProperty("tau_j_t-"+node_jt.getId(), Base64.getEncoder().withoutPadding().encodeToString(tau_j_t.toBytes()));//
        }
        Tool.storePropToFile(updateCTProp,config.getUpdateCtFileName());
        long onlineEncryptEndTime = System.currentTimeMillis();
        return onlineEncryptEndTime - onlineEncryptStartTime;
    }
}

